package com.mfs.proxy.client.transfer.http.response;

import com.mfs.common.constant.Constant;
import com.mfs.common.utills.CollectionUtil;
import com.mfs.common.utills.StringUtil;
import com.mfs.proxy.client.transfer.http.ByteBufferUtil;
import com.mfs.proxy.core.client.config.ClientConfig;
import com.mfs.proxy.core.client.processor.Transfer;
import com.mfs.proxy.core.exceptions.ProxyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Http 响应转换器
 *
 * @author mfs
 * @date 2023/7/20 21:09
 **/
public class HttpResponseTransfer implements Transfer {
    private final static Logger logger = LoggerFactory.getLogger(HttpResponseTransfer.class);
    private List<HttpResponse> resolvedResponses = new ArrayList<>();
    private HttpResponse resolvingResponse = null;
    private ByteBuffer leftByteBuffer = null;
    private ResolveStepEnum nextResolveStep = ResolveStepEnum.RESOLVE_BEGIN;

    @Override
    public void translate(ByteBuffer byteBuffer) {
        /**
         * 1. 合并遗留的字节和新传入的字节
         * */
        ByteBuffer mergedByteBuffer = ByteBuffer.allocate(byteBuffer.capacity() + (leftByteBuffer == null ? 0 : leftByteBuffer.capacity()));
        if (leftByteBuffer != null) {
            mergedByteBuffer.put(leftByteBuffer);
        }
        mergedByteBuffer.put(byteBuffer);
        mergedByteBuffer.flip();
        boolean end = false;

        while (!end) {
            switch (nextResolveStep){
                case RESOLVE_BEGIN:
                    resolvingResponse = new HttpResponse();
                    nextResolveStep = ResolveStepEnum.RESOLVE_RESPONSE_LINE;
                    break;
                case RESOLVE_RESPONSE_LINE:
                    if (resolveResponseLine(resolvingResponse.getHttpResponseLine(), mergedByteBuffer)) {
                        leftByteBuffer = ByteBuffer.wrap(mergedByteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                        end = true;
                    } else {
                        nextResolveStep = ResolveStepEnum.RESOLVE_RESPONSE_HEDA;
                    }
                    break;
                case RESOLVE_RESPONSE_HEDA:
                    if (resolveResponseHeader(resolvingResponse.getHttpResponseHeader(), mergedByteBuffer)) {
                        leftByteBuffer = ByteBuffer.wrap(mergedByteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                        end = true;
                    } else if(resolvingResponse.getHttpResponseHeader().isCompleted()) {

                        nextResolveStep = ResolveStepEnum.RESOLVE_RESPONSE_BODY;
                    }
                    break;
                case RESOLVE_RESPONSE_BODY:
                    // 暂不解析,仅字节赋值，需要时调用请求的decode方法解析
                    String bodyLength = resolvingResponse.getHttpResponseBody().getContentType();
                    boolean chunked = resolvingResponse.getHttpResponseBody().isChunked();
                    if (StringUtil.isInt(bodyLength)) {
                        int length = Integer.valueOf(bodyLength);
                        if (mergedByteBuffer.limit() - mergedByteBuffer.position() < length) {
                            leftByteBuffer = ByteBuffer.wrap(mergedByteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                            end = true;
                        } else {
                            byte[] body = new byte[length];
                            mergedByteBuffer.get(body);
                            resolvingResponse.getHttpResponseBody().setBytes(body);
                            nextResolveStep = ResolveStepEnum.RESOLVE_END;
                        }
                    } else if (chunked) {
                        nextResolveStep = ResolveStepEnum.RESOLVE_CHUNKED_BLOCK;
                    } else {
                        logger.error("不支持的请求体格式:\n{}", resolvingResponse);
                    }
                    break;
                case RESOLVE_CHUNKED_BLOCK:
                    if (resolvingResponse.getHttpResponseBody().enableBodyDecode()) {
                        // 如果响应体是可以解析的则等待解析完整个请求再转发请求
                        HttpResponseBody httpResponseBody = resolvingResponse.getHttpResponseBody();
                        while (!httpResponseBody.isCompleted()) {
                            if (resolveChunkedBlock(httpResponseBody, mergedByteBuffer)) {
                                leftByteBuffer = ByteBuffer.wrap(mergedByteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                                end = true;
                                break;
                            }
                        }
                        if (httpResponseBody.isCompleted()) {
                            nextResolveStep = ResolveStepEnum.RESOLVE_END;
                        }
                    } else {
                        // 响应体不能解析，则能解析多少就转发多少，不处理拆包问题
                        resolvedResponses.add(transfer(resolvingResponse));

                        resolvingResponse = new HttpResponse();
                        resolvingResponse.setChunkedBlock(true);
                        HttpResponseBody httpResponseBody = resolvingResponse.getHttpResponseBody();
                        while (!resolveChunkedBlock(httpResponseBody, mergedByteBuffer)) {
                            if (httpResponseBody.isCompleted()) {
                                resolvedResponses.add(resolvingResponse);
                                nextResolveStep = ResolveStepEnum.RESOLVE_BEGIN;
                                break;
                            }
                        }
                        if (!httpResponseBody.isCompleted()) {
                            leftByteBuffer = ByteBuffer.wrap(mergedByteBuffer.array(), mergedByteBuffer.position(), mergedByteBuffer.limit() - mergedByteBuffer.position());
                            end = true;
                        }
                    }
                    break;
                case RESOLVE_END:
                    resolvedResponses.add(transfer(resolvingResponse));
                    resolvingResponse = null;
                    nextResolveStep = ResolveStepEnum.RESOLVE_BEGIN;
                    break;
            }
        }
    }

    private HttpResponse transfer(HttpResponse httpResponse) {
        return targetAddressReplace(httpResponse);
    }

    /**
     * 使用代理地址替换响应头、响应体中的目标地址
     * */
    private HttpResponse targetAddressReplace(HttpResponse httpResponse) {
        String proxyAddress = ClientConfig.getInstance().getProxyHandle().getProxyIp() + Constant.COLON + ClientConfig.getInstance().getProxyHandle().getProxyPort();
        String targetAddress = ClientConfig.getInstance().getTargetIp() + Constant.COLON + ClientConfig.getInstance().getTargetPort();

        httpResponse.getHttpResponseHeader().heads().stream().forEach(headName -> {
            String headValue = httpResponse.getHttpResponseHeader().getHead(headName);
            httpResponse.getHttpResponseHeader().putHead(headName, headValue.replaceAll(targetAddress, proxyAddress));
        });
        String body = httpResponse.getHttpResponseBody().toString();
        if (body != null) {
            httpResponse.getHttpResponseBody().setBody(body.replaceAll(targetAddress, proxyAddress));
        }
        return httpResponse;
    }

    /**
     * 解析请求行
     * @return true, 剩余的字节已经无法完整的解析为一行，false，剩余的部分仍可继续解析
     */
    private boolean resolveResponseLine(HttpResponseLine httpResponseLine, ByteBuffer byteBuffer) {
        byte[] requestLineByte = ByteBufferUtil.getALine(byteBuffer);
        if (requestLineByte == null) {
            return true;
        }

        try {
            String responseLineString = new String(requestLineByte, "ascii");
            String[] split = responseLineString.split("\\s+");
            if (split.length < 2) {
                logger.error("错误的HTTP响应行{},{}", Arrays.toString(requestLineByte), responseLineString);
                throw new ProxyException("错误的HTTP响应行");
            }
            httpResponseLine.setVersion(split[0]);
            httpResponseLine.setStatus(Integer.valueOf(split[1]));
            if (split.length > 2) {
                httpResponseLine.setDescribe(split[2].replace("\r\n", ""));
            }
            return false;
        } catch (UnsupportedEncodingException e) {
        }
        return false;
    }

    /**
     * 解析请求头
     * @return true, 剩余的字节已经无法完整的解析为一行，false，剩余的部分仍可继续解析
     */
    private boolean resolveResponseHeader(HttpResponseHeader httprequestheader, ByteBuffer byteBuffer) {
        byte[] aHeadByte = ByteBufferUtil.getALine(byteBuffer);
        if (aHeadByte == null) {
            return true;
        }

        try {
            String aHeadString = new String(aHeadByte, "ascii");
            if (aHeadString.length() > 1 && aHeadString.charAt(0) == '\r' && aHeadString.charAt(1) == '\n') {
                httprequestheader.setCompleted(true);
                return false;
            }
            String[] split = aHeadString.split(":\\s", 2);
            if (split.length < 2) {
                logger.error("错误的HTTP响应头{}", Arrays.toString(aHeadByte));
                throw new ProxyException("错误的HTTP响应头");
            }
            httprequestheader.putHead(split[0].toLowerCase(), split[1].replace("\r\n", ""));
            return false;
        } catch (UnsupportedEncodingException e) {
        }
        return false;
    }

    /**
     *
     * @return true, 剩余的字节已经无法完整的解析为一块，false，剩余的部分仍可继续解析
     * */
    private boolean resolveChunkedBlock(HttpResponseBody httpResponseBody, ByteBuffer byteBuffer) {
        byte[] chunkedLength = ByteBufferUtil.getALine(byteBuffer);
        if (chunkedLength == null) {
            return true;
        }
        byte[] chunkedData = ByteBufferUtil.getALine(byteBuffer);
        if (chunkedData == null) {
            // 数据块不完整，回退解析的块长度
            byteBuffer.position(byteBuffer.position() - chunkedLength.length);
            return true;
        }
        int oldLength = httpResponseBody.getBytes().length;
        byte[] bytes = new byte[oldLength + chunkedLength.length + chunkedData.length];
        if (oldLength > 0) {
            System.arraycopy(httpResponseBody.getBytes(), 0, bytes, 0, oldLength);
        }
        System.arraycopy(chunkedLength, 0, bytes, oldLength, chunkedLength.length);
        System.arraycopy(chunkedData, 0, bytes, oldLength + chunkedLength.length, chunkedData.length);
        httpResponseBody.setBytes(bytes);
        try {
            String s = new String(chunkedLength, "ascii");
            s = s.replaceAll("\r\n", "");
            Long length = Long.valueOf(s, 16);
            if (length == Constant.ZERO) {
                httpResponseBody.setCompleted(true);
            }
        } catch (UnsupportedEncodingException e) {
        }
        return false;
    }

    @Override
    public boolean hasNext() {
        return CollectionUtil.isNotEmpty(resolvedResponses);
    }

    @Override
    public ByteBuffer next() {
        return hasNext() ? ByteBuffer.wrap(resolvedResponses.remove(0).getBytes()) : null;
    }

    enum ResolveStepEnum {
        RESOLVE_BEGIN,
        RESOLVE_RESPONSE_LINE,
        RESOLVE_RESPONSE_HEDA,
        RESOLVE_RESPONSE_BODY,
        RESOLVE_CHUNKED_BLOCK,
        RESOLVE_END;
    }
}
